import pandas as pd
import plotly.express as px
from plotly.graph_objects import Figure
from nicegui import ui, app
import os

# 设置中文字体支持
import plotly.io as pio

pio.templates.default = "plotly_white"

# 全局变量存储数据和图表
global_data = None
global_figures = {}
plot_references = {}  # 存储图表组件引用


def load_processed_data(file_path):
    """加载预处理后的数据"""
    try:
        df = pd.read_csv(file_path)
        print(f"成功加载预处理数据，共{len(df)}行{len(df.columns)}列")
        return df
    except FileNotFoundError:
        print(f"错误：未找到文件 '{file_path}'")
        return None
    except Exception as e:
        print(f"数据加载失败: {str(e)}")
        return None


def create_3d_visualization(df):
    """创建3D可视化图表 - 使用多样化颜色"""
    numeric_cols = df.select_dtypes(include='number').columns.tolist()
    if len(numeric_cols) < 3:
        print("警告：数值列不足，无法创建3D图表")
        return None

    x_col, y_col, z_col = numeric_cols[:3]
    # 替换为中文列名
    x_col_name = '乘客ID' if x_col == 'Passengerid' else x_col
    y_col_name = '年龄' if y_col == 'Age' else y_col
    z_col_name = '票价' if z_col == 'Fare' else z_col

    print(f"创建3D图表：x={x_col_name}, y={y_col_name}, z={z_col_name}")

    # 定义悬停信息
    hover_data = {
        x_col: True,
        y_col: True,
        z_col: True
    }

    # 使用定性色阶，为不同类别提供鲜明对比
    color_col = '生存状态' if 'Survived' in df.columns else None
    color_map = {0: '#ef4444', 1: '#10b981'}  # 自定义颜色映射

    fig = px.scatter_3d(
        df,
        x=x_col, y=y_col, z=z_col,
        color=color_col,
        color_discrete_map=color_map,
        hover_data=hover_data,
        title=f'3D数据分布：{x_col_name}-{y_col_name}-{z_col_name}'
    )

    # 自定义布局和颜色 - 全部中文
    fig.update_layout(
        scene=dict(
            xaxis_title=x_col_name,
            yaxis_title=y_col_name,
            zaxis_title=z_col_name,
            camera=dict(eye=dict(x=1.5, y=1.5, z=0.8))
        ),
        font=dict(family="SimHei", size=12),
        legend=dict(
            title='图例',
            orientation='h',
            yanchor='bottom',
            y=1.02,
            xanchor='right',
            x=1
        )
    )

    # 如果有生存状态列，修改图例名称
    if color_col:
        fig.for_each_trace(lambda t: t.update(name="生存" if t.name == "1" else "死亡"))

    # 自定义悬停标签
    fig.update_traces(
        hovertemplate=f"{x_col_name}: %{{x}}<br>{y_col_name}: %{{y}}<br>{z_col_name}: %{{z}}<extra></extra>"
    )

    return fig


def create_histogram(df):
    """创建直方图 - 使用渐变颜色"""
    numeric_cols = df.select_dtypes(include='number').columns.tolist()
    if not numeric_cols:
        print("警告：没有数值列，无法创建直方图")
        return None

    x_col = 'Fare' if 'Fare' in df.columns else numeric_cols[0]
    x_col_name = '票价' if x_col == 'Fare' else x_col

    print(f"创建直方图：x={x_col_name}")

    # 定义悬停信息
    hover_data = {
        x_col: True,
        'count': True
    }

    # 使用连续色阶，展示数据分布密度
    fig = px.histogram(
        df,
        x=x_col,
        title=f'{x_col_name}分布直方图',
        nbins=30,
        color_discrete_sequence=['#3b82f6']  # 使用蓝色系
    )

    # 自定义颜色和布局 - 全部中文
    fig.update_traces(
        marker=dict(
            color=df[x_col],
            colorscale='Viridis',  # 使用渐变色
            showscale=True,
            colorbar=dict(title=x_col_name)
        )
    )

    fig.update_layout(
        font=dict(family="SimHei", size=12),
        bargap=0.1,
        xaxis_title=x_col_name,
        yaxis_title='数量'
    )

    # 自定义悬停标签
    fig.update_traces(
        hovertemplate=f"{x_col_name}: %{{x}}<br>数量: %{{y}}<extra></extra>"
    )

    return fig


def create_grouped_chart(df):
    """创建分组柱状图 - 使用对比鲜明的颜色"""
    fig = Figure()
    if 'Survived' in df.columns and 'Pclass' in df.columns:
        print("创建分组柱状图：生存率 vs 船舱等级")
        survival_rate = df.groupby('Pclass')['Survived'].mean().reset_index()

        # 替换列名
        survival_rate = survival_rate.rename(columns={
            'Pclass': '船舱等级',
            'Survived': '生存率'
        })

        # 使用对比鲜明的颜色
        fig = px.bar(
            survival_rate,
            x='船舱等级', y='生存率',
            title='各船舱等级生存率',
            color='船舱等级',
            color_discrete_sequence=['#ef4444', '#f59e0b', '#10b981']  # 红、橙、绿
        )

        # 自定义悬停标签
        fig.update_traces(
            hovertemplate="船舱等级: %{x}<br>生存率: %{y:.2%}<extra></extra>"
        )
    else:
        numeric_cols = df.select_dtypes(include='number').columns.tolist()
        if numeric_cols and len(df) > 1:
            # 退化为简单柱状图
            print("创建简单柱状图：无生存率数据")
            sample_col = numeric_cols[0]
            sample_col_name = '票价' if sample_col == 'Fare' else '年龄' if sample_col == 'Age' else sample_col

            # 使用pd.cut()并将区间转换为字符串
            group_data = df.copy()
            group_data[f'{sample_col_name}分组'] = pd.cut(group_data[sample_col], 5).astype(str)
            group_counts = group_data.groupby(f'{sample_col_name}分组')[sample_col].count().reset_index(name='数量')

            # 使用彩虹色阶
            fig = px.bar(
                group_counts,
                x=f'{sample_col_name}分组', y='数量',
                title=f'{sample_col_name}分组统计',
                color=f'{sample_col_name}分组',
                color_discrete_sequence=px.colors.qualitative.Set3  # 使用彩虹色
            )

            # 自定义悬停标签
            fig.update_traces(
                hovertemplate=f"{sample_col_name}分组: %{{x}}<br>数量: %{{y}}<extra></extra>"
            )
        else:
            print("警告：数据不足，无法创建柱状图")
            fig.add_annotation(
                text="暂无分组分析数据",
                xref="paper", yref="paper",
                x=0.5, y=0.5, showarrow=False,
                font=dict(size=16)
            )

    # 自定义布局和颜色 - 全部中文
    fig.update_layout(
        font=dict(family="SimHei", size=12),
        legend=dict(
            title='分组',
            orientation='h',
            yanchor='bottom',
            y=1.02,
            xanchor='right',
            x=1
        ),
        xaxis_title='',
        yaxis_title='数量'
    )
    return fig


def create_advanced_filters(df, update_callback, page_name):
    """创建高级筛选控件 - 支持票价、年龄和乘客ID筛选"""
    with ui.row().classes('w-full p-4 bg-blue-50'):
        with ui.column().classes('w-1/4'):
            ui.label("数据筛选").classes('text-lg font-medium')

            # 获取数据范围
            age_min, age_max = 0, 100
            fare_min, fare_max = 0, 1000
            passenger_id_min, passenger_id_max = 0, 1000

            if 'Age' in df.columns:
                age_min, age_max = df['Age'].min(), df['Age'].max()
            if 'Fare' in df.columns:
                fare_min, fare_max = df['Fare'].min(), df['Fare'].max()
            if 'Passengerid' in df.columns:
                passenger_id_min, passenger_id_max = df['Passengerid'].min(), df['Passengerid'].max()

            # 年龄筛选 - 初始化为范围选择，设置合理的初始值
            ui.label("年龄范围")
            age_range = ui.slider(min=age_min, max=age_max, value=[age_min, age_max]).props('range')

            # 票价筛选 - 初始化为范围选择，设置合理的初始值
            ui.label("票价范围")
            fare_range = ui.slider(min=fare_min, max=fare_max, value=[fare_min, fare_max]).props('range')

            # 乘客ID筛选 - 初始化为范围选择，设置合理的初始值
            ui.label("乘客ID范围")
            passenger_id_range = ui.slider(min=passenger_id_min, max=passenger_id_max,
                                           value=[passenger_id_min, passenger_id_max]).props('range')

            # 筛选按钮
            filter_btn = ui.button("应用筛选", on_click=lambda: update_callback(
                age_range.value, fare_range.value, passenger_id_range.value
            ))

    return age_range, fare_range, passenger_id_range, filter_btn


def create_3d_page(df):
    """创建3D图表页面"""

    @ui.page('/3d')
    def page_3d():
        with ui.header(elevated=True).classes('bg-blue-500 text-white'):
            ui.label("3D数据可视化").classes('text-2xl font-bold')
            with ui.row():
                ui.link("首页", "/")
                ui.link("直方图", "/histogram")
                ui.link("分组分析", "/grouped")

        # 主内容区
        with ui.column().classes('w-full') as container:
            if '3d' in global_figures and global_figures['3d']:
                plot = ui.plotly(figure=global_figures['3d']).classes('w-full h-[700px]')
                plot_references['3d'] = plot  # 保存图表引用
            else:
                ui.label("无法创建3D可视化，请检查数据是否包含足够的数值列").classes('text-xl text-red-500')

        # 筛选控件
        def update_3d_chart(age_value, fare_value, passenger_id_value):
            try:
                df_filtered = global_data.copy()

                # 应用年龄筛选
                if 'Age' in df_filtered.columns and isinstance(age_value, list) and len(age_value) == 2:
                    age_min, age_max = age_value
                    df_filtered = df_filtered[
                        (df_filtered['Age'] >= age_min) &
                        (df_filtered['Age'] <= age_max)
                        ]

                # 应用票价筛选
                if 'Fare' in df_filtered.columns and isinstance(fare_value, list) and len(fare_value) == 2:
                    fare_min, fare_max = fare_value
                    df_filtered = df_filtered[
                        (df_filtered['Fare'] >= fare_min) &
                        (df_filtered['Fare'] <= fare_max)
                        ]

                # 应用乘客ID筛选
                if 'Passengerid' in df_filtered.columns and isinstance(passenger_id_value, list) and len(
                        passenger_id_value) == 2:
                    pid_min, pid_max = passenger_id_value
                    df_filtered = df_filtered[
                        (df_filtered['Passengerid'] >= pid_min) &
                        (df_filtered['Passengerid'] <= pid_max)
                        ]

                print(f"3D图表筛选后数据：{len(df_filtered)}行")

                if len(df_filtered) == 0:
                    print("警告：筛选后数据为空")
                    ui.notify("筛选条件过严，没有匹配的数据", type="warning")
                    return

                # 重新生成图表
                fig_3d = create_3d_visualization(df_filtered)
                if fig_3d:
                    global_figures['3d'] = fig_3d
                    if '3d' in plot_references:
                        plot_references['3d'].figure = fig_3d  # 更新figure属性
                        plot_references['3d'].update()  # 调用update方法刷新图表
                    filter_btn.set_text(f"已筛选 ({len(df_filtered)}行)")
                else:
                    ui.notify("筛选后数据不足，无法创建3D图表", type="warning")

            except Exception as e:
                print(f"3D图表筛选过程出错: {str(e)}")
                ui.notify(f"筛选出错: {str(e)}", type="error")

        # 创建筛选控件
        age_range, fare_range, passenger_id_range, filter_btn = create_advanced_filters(df, update_3d_chart, '3d')


def create_histogram_page(df):
    """创建直方图页面"""

    @ui.page('/histogram')
    def page_histogram():
        with ui.header(elevated=True).classes('bg-blue-500 text-white'):
            ui.label("直方图数据可视化").classes('text-2xl font-bold')
            with ui.row():
                ui.link("首页", "/")
                ui.link("3D图表", "/3d")
                ui.link("分组分析", "/grouped")

        # 主内容区
        with ui.column().classes('w-full') as container:
            if 'histogram' in global_figures and global_figures['histogram']:
                plot = ui.plotly(figure=global_figures['histogram']).classes('w-full h-[700px]')
                plot_references['histogram'] = plot  # 保存图表引用
            else:
                ui.label("无法创建直方图，请检查数据是否包含数值列").classes('text-xl text-red-500')

        # 筛选控件
        def update_histogram(age_value, fare_value, passenger_id_value):
            try:
                df_filtered = global_data.copy()

                # 应用年龄筛选
                if 'Age' in df_filtered.columns and isinstance(age_value, list) and len(age_value) == 2:
                    age_min, age_max = age_value
                    df_filtered = df_filtered[
                        (df_filtered['Age'] >= age_min) &
                        (df_filtered['Age'] <= age_max)
                        ]

                # 应用票价筛选
                if 'Fare' in df_filtered.columns and isinstance(fare_value, list) and len(fare_value) == 2:
                    fare_min, fare_max = fare_value
                    df_filtered = df_filtered[
                        (df_filtered['Fare'] >= fare_min) &
                        (df_filtered['Fare'] <= fare_max)
                        ]

                # 应用乘客ID筛选
                if 'Passengerid' in df_filtered.columns and isinstance(passenger_id_value, list) and len(
                        passenger_id_value) == 2:
                    pid_min, pid_max = passenger_id_value
                    df_filtered = df_filtered[
                        (df_filtered['Passengerid'] >= pid_min) &
                        (df_filtered['Passengerid'] <= pid_max)
                        ]

                print(f"直方图筛选后数据：{len(df_filtered)}行")

                if len(df_filtered) == 0:
                    print("警告：筛选后数据为空")
                    ui.notify("筛选条件过严，没有匹配的数据", type="warning")
                    return

                # 重新生成图表
                fig_hist = create_histogram(df_filtered)
                if fig_hist:
                    global_figures['histogram'] = fig_hist
                    if 'histogram' in plot_references:
                        plot_references['histogram'].figure = fig_hist  # 更新figure属性
                        plot_references['histogram'].update()  # 调用update方法刷新图表
                    filter_btn.set_text(f"已筛选 ({len(df_filtered)}行)")
                else:
                    ui.notify("筛选后数据不足，无法创建直方图", type="warning")

            except Exception as e:
                print(f"直方图筛选过程出错: {str(e)}")
                ui.notify(f"筛选出错: {str(e)}", type="error")

        # 创建筛选控件
        age_range, fare_range, passenger_id_range, filter_btn = create_advanced_filters(df, update_histogram,
                                                                                        'histogram')


def create_grouped_page(df):
    """创建分组分析页面"""

    @ui.page('/grouped')
    def page_grouped():
        with ui.header(elevated=True).classes('bg-blue-500 text-white'):
            ui.label("分组数据分析").classes('text-2xl font-bold')
            with ui.row():
                ui.link("首页", "/")
                ui.link("3D图表", "/3d")
                ui.link("直方图", "/histogram")

        # 主内容区
        with ui.column().classes('w-full') as container:
            if 'grouped' in global_figures and global_figures['grouped']:
                plot = ui.plotly(figure=global_figures['grouped']).classes('w-full h-[700px]')
                plot_references['grouped'] = plot  # 保存图表引用
            else:
                ui.label("无法创建分组分析图表，请检查数据是否包含足够的分类列").classes('text-xl text-red-500')

        # 筛选控件
        def update_grouped_chart(age_value, fare_value, passenger_id_value):
            try:
                df_filtered = global_data.copy()

                # 应用年龄筛选
                if 'Age' in df_filtered.columns and isinstance(age_value, list) and len(age_value) == 2:
                    age_min, age_max = age_value
                    df_filtered = df_filtered[
                        (df_filtered['Age'] >= age_min) &
                        (df_filtered['Age'] <= age_max)
                        ]

                # 应用票价筛选
                if 'Fare' in df_filtered.columns and isinstance(fare_value, list) and len(fare_value) == 2:
                    fare_min, fare_max = fare_value
                    df_filtered = df_filtered[
                        (df_filtered['Fare'] >= fare_min) &
                        (df_filtered['Fare'] <= fare_max)
                        ]

                # 应用乘客ID筛选
                if 'Passengerid' in df_filtered.columns and isinstance(passenger_id_value, list) and len(
                        passenger_id_value) == 2:
                    pid_min, pid_max = passenger_id_value
                    df_filtered = df_filtered[
                        (df_filtered['Passengerid'] >= pid_min) &
                        (df_filtered['Passengerid'] <= pid_max)
                        ]

                print(f"分组分析筛选后数据：{len(df_filtered)}行")

                if len(df_filtered) == 0:
                    print("警告：筛选后数据为空")
                    ui.notify("筛选条件过严，没有匹配的数据", type="warning")
                    return

                # 重新生成图表
                fig_grouped = create_grouped_chart(df_filtered)
                if fig_grouped:
                    global_figures['grouped'] = fig_grouped
                    if 'grouped' in plot_references:
                        plot_references['grouped'].figure = fig_grouped  # 更新figure属性
                        plot_references['grouped'].update()  # 调用update方法刷新图表
                    filter_btn.set_text(f"已筛选 ({len(df_filtered)}行)")
                else:
                    ui.notify("筛选后数据不足，无法创建分组图表", type="warning")

            except Exception as e:
                print(f"分组分析筛选过程出错: {str(e)}")
                ui.notify(f"筛选出错: {str(e)}", type="error")

        # 创建筛选控件
        age_range, fare_range, passenger_id_range, filter_btn = create_advanced_filters(df, update_grouped_chart,
                                                                                        'grouped')


def create_main_page():
    """创建主页 - 使用ui.link实现卡片跳转"""

    @ui.page('/')
    def page_main():
        with ui.header(elevated=True).classes('bg-blue-500 text-white'):
            ui.label("大数据课程设计可视化平台").classes('text-2xl font-bold')

        with ui.column().classes('w-full items-center justify-center p-8'):
            ui.label("请选择要查看的图表类型").classes('text-2xl font-medium mb-8')

            with ui.grid(columns=3).classes('w-full gap-8'):
                # 3D可视化卡片 - 使用ui.link
                with ui.link(target='/3d').classes('no-underline w-full'):
                    with ui.card().classes(
                            'w-full cursor-pointer hover:shadow-lg transition-all duration-300 hover:-translate-y-1 bg-white rounded-xl overflow-hidden'):
                        with ui.card_section().classes('h-full flex flex-col items-center justify-center p-6'):
                            ui.label("3D可视化").classes('text-xl font-bold mb-2')
                            ui.icon('3d_rotation', size='4xl').classes('mb-3 text-blue-500')
                            ui.label("查看三维数据分布").classes('text-gray-600 text-center')

                # 直方图卡片 - 使用ui.link
                with ui.link(target='/histogram').classes('no-underline w-full'):
                    with ui.card().classes(
                            'w-full cursor-pointer hover:shadow-lg transition-all duration-300 hover:-translate-y-1 bg-white rounded-xl overflow-hidden'):
                        with ui.card_section().classes('h-full flex flex-col items-center justify-center p-6'):
                            ui.label("直方图").classes('text-xl font-bold mb-2')
                            ui.icon('insert_chart', size='4xl').classes('mb-3 text-green-500')
                            ui.label("查看数据分布情况").classes('text-gray-600 text-center')

                # 分组分析卡片 - 使用ui.link
                with ui.link(target='/grouped').classes('no-underline w-full'):
                    with ui.card().classes(
                            'w-full cursor-pointer hover:shadow-lg transition-all duration-300 hover:-translate-y-1 bg-white rounded-xl overflow-hidden'):
                        with ui.card_section().classes('h-full flex flex-col items-center justify-center p-6'):
                            ui.label("分组分析").classes('text-xl font-bold mb-2')
                            ui.icon('bar_chart', size='4xl').classes('mb-3 text-orange-500')
                            ui.label("查看不同类别数据对比").classes('text-gray-600 text-center')


if __name__ in {"__main__", "__mp_main__"}:
    # 替换为实际预处理后的CSV文件路径
    processed_file_path = r'C:\Users\Lenovoo\Desktop\邵一川\课设\processed_data_20250617_164540.csv'

    # 1. 加载预处理数据
    global_data = load_processed_data(processed_file_path)
    if global_data is not None and not global_data.empty:
        # 2. 创建初始图表
        print("正在创建初始可视化图表...")
        global_figures['3d'] = create_3d_visualization(global_data)
        global_figures['histogram'] = create_histogram(global_data)
        global_figures['grouped'] = create_grouped_chart(global_data)

        # 3. 创建各个页面
        create_main_page()
        create_3d_page(global_data)
        create_histogram_page(global_data)
        create_grouped_page(global_data)

        # 4. 启动应用
        print("启动应用...")
        ui.run(title="大数据可视化平台", port=8080, reload=True)
    else:
        print("数据加载失败，程序退出")